/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Tencent is pleased to support the open source community by making behaviac available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at http://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef _ENGINESERVICES_MEMBER_H_
#define _ENGINESERVICES_MEMBER_H_


#include "behaviac/base/object/typehandler.h"
#include "behaviac/base/object/uitype.h"

#include "behaviac/property/property.h"

#include "behaviac/network/network.h"

// Used in ContainedMember and GroupMember
#define CreateChildNode true
#define DoNotCreateChildNode false

class CTagObject;

namespace behaviac
{
	class Property;
}

template<typename T>
inline float FloatReturnSubtract(const T& lhs, const T& rhs)
{
	BEHAVIAC_UNUSED_VAR(lhs);
	BEHAVIAC_UNUSED_VAR(rhs);

	return 0.0f;
}

inline float FloatReturnSubtract(const int& lhs, const int& rhs)
{
	int result = lhs > rhs ? (lhs - rhs) : rhs - lhs;

	return (float)result;
}

inline float FloatReturnSubtract(const unsigned int& lhs, const unsigned int& rhs)
{
	unsigned int result = lhs > rhs ? (lhs - rhs) : rhs - lhs;

	return (float)result;
}

template<>
inline float FloatReturnSubtract(const short& lhs, const short& rhs)
{
	short result = lhs > rhs ? (lhs - rhs) : rhs - lhs;

	return (float)result;
}

template<>
inline float FloatReturnSubtract(const unsigned short& lhs, const unsigned short& rhs)
{
	unsigned short result = lhs > rhs ? (lhs - rhs) : rhs - lhs;

	return (float)result;
}

template<>
inline float FloatReturnSubtract(const char& lhs, const char& rhs)
{
	char result = lhs > rhs ? (lhs - rhs) : rhs - lhs;

	return (float)result;
}

template<>
inline float FloatReturnSubtract(const unsigned char& lhs, const unsigned char& rhs)
{
	unsigned char result = lhs > rhs ? (lhs - rhs) : rhs - lhs;

	return (float)result;
}

template<>
inline float FloatReturnSubtract(const long& lhs, const long& rhs)
{
	long result = lhs > rhs ? (lhs - rhs) : rhs - lhs;

	return (float)result;
}

template<>
inline float FloatReturnSubtract(const unsigned long& lhs, const unsigned long& rhs)
{
	unsigned long result = lhs > rhs ? (lhs - rhs) : rhs - lhs;

	return (float)result;
}
/////////////////////////////////////////////////////////
///////////////    MEMBER INTERFACE    ////////////////
/////////////////////////////////////////////////////////
class BEHAVIAC_API CMemberBase
{
	BEHAVIAC_DECLARE_MEMORY_OPERATORS(CMemberBase);

public:
	CMemberBase(const char* propertyName, const char* classFullName)
		: m_parentType(PT_INVALID), m_bStatic(false), m_netRole(behaviac::NET_ROLE_DEFAULT), 
		m_classFullName(classFullName), m_instaceName(classFullName), m_propertyID(propertyName), m_range(1.0f)
	{
		BEHAVIAC_ASSERT(m_classFullName);
	}

	CMemberBase(const CMemberBase& copy) :
		m_parentType(copy.m_parentType), m_bStatic(copy.m_bStatic), m_netRole(copy.m_netRole), 
		m_classFullName(copy.m_classFullName), m_instaceName(copy.m_instaceName), m_propertyID(copy.m_propertyID),
		m_displayName(copy.m_displayName), m_desc(copy.m_desc), m_range(copy.m_range)
	{}

	virtual ~CMemberBase()
	{
	}

	virtual void        Load(CTagObject* parent, const ISerializableNode* node) = 0;
	virtual void        Save(const CTagObject* parent, ISerializableNode* node) = 0;
	virtual void        LoadState(CTagObject* parent, const ISerializableNode* node) = 0;
	virtual void        SaveState(const CTagObject* parent, ISerializableNode* node) = 0;
	virtual void        GetUiInfo(CTagTypeDescriptor::TypesMap_t* types, const CTagObject* parent, const XmlNodeRef& xmlNode) = 0;
	virtual void        GetMethodsDescription(CTagTypeDescriptor::TypesMap_t* types, const CTagObject* parent, const XmlNodeRef& xmlNode) = 0;
	virtual CMemberBase* clone() const = 0;
	virtual CSerializationID GetID() const
	{
		return m_propertyID;
	}

	virtual void        Set(const CTagObject* parent, const void* value, int typeId) const
	{
		BEHAVIAC_UNUSED_VAR(parent);
		BEHAVIAC_UNUSED_VAR(value);
		BEHAVIAC_UNUSED_VAR(typeId);

		BEHAVIAC_ASSERT(!"Only works with CGenericMembers");
	};

	virtual void*       Get(const CTagObject* parent, int typeId) const
	{
		BEHAVIAC_UNUSED_VAR(parent);
		BEHAVIAC_UNUSED_VAR(typeId);

		BEHAVIAC_ASSERT(!"Only works with CGenericMembers");
		return NULL;
	}

	virtual void        SetVariable(const CTagObject* parent, const void* value, int typeId) const
	{
		BEHAVIAC_UNUSED_VAR(parent);
		BEHAVIAC_UNUSED_VAR(value);
		BEHAVIAC_UNUSED_VAR(typeId);

		BEHAVIAC_ASSERT(!"Only works with CGenericMembers");
	}

	virtual void*       GetVariable(const CTagObject* parent, int typeId) const
	{
		BEHAVIAC_UNUSED_VAR(parent);
		BEHAVIAC_UNUSED_VAR(typeId);

		BEHAVIAC_ASSERT(!"Only works with CGenericMembers");
		return NULL;
	}

	virtual int GetTypeId() const
	{
		BEHAVIAC_ASSERT(!"Only works with CGenericMembers");

		return 0;
	}

	virtual void SetFromProperty(CTagObject* parent, const CTagObject* parHolder, const behaviac::Property* pProperty) const
	{
		BEHAVIAC_UNUSED_VAR(parent);
		BEHAVIAC_UNUSED_VAR(parHolder);
		BEHAVIAC_UNUSED_VAR(pProperty);
		BEHAVIAC_ASSERT(!"implemented by subclass!");
	}

	virtual bool       Equal(const CTagObject* lhs, const CTagObject* rhs) const
	{
		BEHAVIAC_UNUSED_VAR(lhs);
		BEHAVIAC_UNUSED_VAR(rhs);

		BEHAVIAC_ASSERT(!"Only works with CGenericMembers");

		return false;
	}

	ParentType GetParentType() const
	{
		return this->m_parentType;
	}

	virtual behaviac::Property* CreateProperty(const char* defaultValue, bool bConst) const
	{
		BEHAVIAC_UNUSED_VAR(defaultValue);
		BEHAVIAC_UNUSED_VAR(bConst);

		return 0;
	}

	template <typename T>
	float TDifferencePercentage(const behaviac::Property* l, const behaviac::Property* r);

	virtual float DifferencePercentage(const behaviac::Property* l, const behaviac::Property* r)
	{
		BEHAVIAC_UNUSED_VAR(l);
		BEHAVIAC_UNUSED_VAR(r);

		return 0.0f;
	}

	const char* GetClassNameString() const
	{
		return m_classFullName;
	}

	const char* GetInstanceNameString() const
	{
		return m_instaceName.c_str();
	}

	void SetInstanceNameString(const char* name)
	{
		m_instaceName = name;
	}

	const char* GetName() const
	{
		return this->m_propertyID.GetString();
	}

	const behaviac::wstring& GetDisplayName() const
	{
		return this->m_displayName;
	}

	const behaviac::wstring& GetDesc() const
	{
		return this->m_desc;
	}

	bool ISSTATIC() const
	{
		return this->m_bStatic;
	}

	CMemberBase& RANGE(float range)
	{
		this->m_range = range;

		return *this;
	}

	/// deparated, to use DISPLAY_INFO
	CMemberBase& DISPLAYNAME(const wchar_t* displayName)
	{
		BEHAVIAC_UNUSED_VAR(displayName);

		if (displayName)
		{
			m_displayName = displayName;
		}

		return *this;
	}

	/// deparated, to use DISPLAY_INFO
	CMemberBase& DESC(const wchar_t* desc)
	{
		BEHAVIAC_UNUSED_VAR(desc);

		if (desc)
		{
			m_desc = desc;
		}

		return *this;
	}

	CMemberBase& DISPLAY_INFO(const wchar_t* displayName, const wchar_t* desc)
	{
		BEHAVIAC_UNUSED_VAR(displayName);
		BEHAVIAC_UNUSED_VAR(desc);

		if (displayName)
		{
			m_displayName = displayName;
		}

		if (desc)
		{
			m_desc = desc;
		}
		else
		{
			m_desc = m_displayName;
		}

		return *this;
	}

#if BEHAVIAC_ENABLE_NETWORKD
	CMemberBase& NETROLE(behaviac::NetworkRole netRole)
	{
		m_netRole = netRole;

		if (this->m_netRole != behaviac::NET_ROLE_DEFAULT)
		{
			behaviac::Network* pNw = behaviac::Network::GetInstance();
			if (pNw && !pNw->IsSinglePlayer())
			{
				behaviac::string nameTemp;

				if (this->m_className)
				{
					nameTemp = FormatString("%s::%s", this->m_className, m_propertyID.GetString());
				}
				else
				{
					nameTemp = m_propertyID.GetString();
				}

				pNw->RegisterReplicatedProperty(this->m_netRole, nameTemp.c_str());
			}
		}

		return *this;
	}

	behaviac::NetworkRole NETROLE() const
	{
		return m_netRole;
	}

	virtual void ReplicateProperty(behaviac::Agent* pAgent)
	{
		BEHAVIAC_UNUSED_VAR(pAgent);
	}
#endif//#if BEHAVIAC_ENABLE_NETWORKD
protected:
	ParentType					m_parentType;
	bool						m_bStatic;
	behaviac::NetworkRole		m_netRole;
	const char*					m_classFullName;
	behaviac::string			m_instaceName;
	CSerializationID            m_propertyID;

	behaviac::wstring			m_displayName;
	behaviac::wstring			m_desc;

	float						m_range;
};

enum EPersistenceType
{
	EPersistenceType_Description_Load = 1 << 0,
	EPersistenceType_Description_Save = 1 << 1,
	EPersistenceType_Description = EPersistenceType_Description_Load | EPersistenceType_Description_Save,
	EPersistenceType_State_Load = 1 << 2,
	EPersistenceType_State_Save = 1 << 3,
	EPersistenceType_State = EPersistenceType_State_Load | EPersistenceType_State_Save,
	EPersistenceType_UiInfo = 1 << 4,
	EPersistenceType_Description_UiInfo = EPersistenceType_Description | EPersistenceType_UiInfo,

	EPersistenceType_All = EPersistenceType_Description | EPersistenceType_State,
	EPersistenceType_All_UiInfo = EPersistenceType_Description | EPersistenceType_State | EPersistenceType_UiInfo,
};

/////////////////////////////////////////////////////////
////////////////     GENERIC MEMBER      ////////////////
/////////////////////////////////////////////////////////

static const uint32_t DefaultPropertyFlags = EPersistenceType_All;

#define DECLARE_UIWRAPPER(UiDescriptor)																\
	UiGenericType* localWrapper = UiDescriptorAllocate_##UiDescriptor

#define REGISTER_MEMBER(propertyName, memberName, propertyFlags, MemberHandler, UiDescriptor)		\
{																								\
	DECLARE_UIWRAPPER(UiDescriptor);															\
	CMemberBase* _property = CMemberFactory::Create<MemberHandler, propertyFlags>(              \
	objectType::GetClassTypeName(), propertyName, &objectType::memberName, localWrapper);	\
	CTagObjectDescriptor::PushBackMember(ms_members, _property);								\
}

#endif // #ifndef _ENGINESERVICES_MEMBER_H_
